package personal.parser;

import exceptions.*;
import personal.scanner.MyScanner;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

public class SimpleParser implements MyParser {
    @Override
    public String parser(final List<MyScanner.Token> input) throws SyntacticException, SemanticException {
        // 预处理
        if (input.isEmpty()) throw new EmptyExpressionException();
        this.checkBraceAnd3D(input);
        // 初始化数据结构
        Lookahead lookahead = new Lookahead(input, this.parserTable.getGrammar());
        final List<Integer> statuses = new ArrayList<>(); // 状态栈
        final LinkedList<Grammar.Symbol> symbols = new LinkedList<>(); // 符号栈
        final List<String> codes = new ArrayList<>(); // 代码，用于语义运算
        final SemanticAnalyzer semanticAnalyzer = new SemanticAnalyzer(this.parserTable.getGrammar()); // 语法分析器
        this.reducePath = new StringBuilder();
        statuses.add(1);
        symbols.add(this.parserTable.getGrammar().getSymbol("$"));
        // 开始归约
        int deadline = input.size() * this.parserTable.size() * this.parserTable.size();
        for (; deadline > 0; --deadline) {
            reducePath.append(String.format("%s,     %s\n", symbols, lookahead.terminal()));
            ParserTable.Action action = this.parserTable.getAction(statuses.get(statuses.size() - 1), lookahead.terminal()); // 根据状态栈顶、lookahead，查找 Action 表，
            if (action == null) this.exitWithException(symbols, lookahead.terminal()); // 如果是空。那么抛出异常
            if (action instanceof ParserTable.AcceptAction) break; // 如果是接受
            if (action instanceof ParserTable.ShiftAction) { // 如果是移入，
                symbols.add(lookahead.terminal()); // 那么移入
                statuses.add(action.getId()); // 压入新的状态
                codes.add(lookahead.token().getExpression()); // 放入 token 的表达式
                lookahead.update(); // 更新 lookahead
                continue;
            }
            if (!(action instanceof ParserTable.ReduceAction)) throw new UnknownError("动作不是接受、移入、归约");
            // 是归约
            Grammar.Production production = this.parserTable.getGrammar().getProductions().get(action.getId()); // 找到归约项
            for (int i = 0; i < production.rightSide().size(); ++i) { // 设归约项长度为 rSize，栈均弹出 rSize 个元素，
                symbols.remove(symbols.size() - 1);
                statuses.remove(statuses.size() - 1);
            }
            symbols.add(production.leftSide()); // 将归约结果放入符号栈顶，
            statuses.add(this.parserTable.getGoto(statuses.get(statuses.size() - 1), production.leftSide())); // 根据状态栈顶、符号栈顶查找 Goto 表，并将跳转状态放入状态栈顶
            semanticAnalyzer.analysis(codes, production); // 语义运算，用于修改 token 栈
        }
        if (deadline == 0) throw new UnknownError("归约过程超时");
        if (codes.size() != 1) throw new SemanticException();
        return codes.get(0);
    }

    /**
     * 预处理，并抛出部分异常
     *
     * @param input token 列表
     * @throws MissingLeftParenthesisException  缺少左括号
     * @throws MissingRightParenthesisException 缺少右括号
     * @throws TrinaryOperationException        三元运算符使用错误
     */
    private void checkBraceAnd3D(final List<MyScanner.Token> input) throws MissingLeftParenthesisException, MissingRightParenthesisException, TrinaryOperationException {
        int cntBrace = 0, cnt3D = 0; // 括号计数，三元运算符计数
        for (MyScanner.Token token : input) {
            switch (token.getExpression()) {
                case "(" -> --cntBrace;
                case ")" -> ++cntBrace;
                case "?" -> --cnt3D;
                case ":" -> ++cnt3D;
            }
            if (cntBrace > 0) throw new MissingLeftParenthesisException();
            if (cnt3D > 0) throw new TrinaryOperationException();
        }
        if (cntBrace > 0) throw new MissingLeftParenthesisException();
        if (cntBrace < 0) throw new MissingRightParenthesisException();
        if (cnt3D != 0) throw new TrinaryOperationException();
    }

    /**
     * 归约路径记录
     */
    private StringBuilder reducePath;

    /**
     * 负责根据现状抛出异常
     *
     * @param symbols   符号
     * @param lookahead 下一位
     */
    private void exitWithException(final LinkedList<Grammar.Symbol> symbols, Grammar.Terminal lookahead) throws SyntacticException, TypeMismatchedException {
        // 特殊优化：函数没有左括号
        switch (symbols.getLast().getExpression()) {
            case "sin", "cos", "max", "min":
                if (lookahead.getExpression().equals("(")) throw new FunctionCallException();
            default:
        }

        // 特殊优化：sin、cos 没有没有内容
        if (symbols.size() >= 2) {
            List<String> buffer = new ArrayList<>(symbols.subList(symbols.size() - 2, symbols.size()).stream().map(Grammar.Symbol::getExpression).toList());
            buffer.add(lookahead.getExpression());
            if (buffer.equals(List.of("sin", "(", ")"))) throw new MissingOperandException();
            if (buffer.equals(List.of("max", "(", ")"))) throw new MissingOperandException();
        }

        // 特殊优化：( ?
        if (symbols.getLast().getExpression().equals("(") && lookahead.getExpression().equals("?")) {
            throw new MissingOperandException();
        }
//        // 特殊优化：decimal :
//        if (symbols.getLast().getExpression().equals("(") && lookahead.getExpression().equals("?")) {
//            throw new MissingOperandException();
//        }
        if (this.detectArithExpr(symbols)) {
            // [$, ArithExpr, ^, (, ArithExpr, )],     decimal
            // [$, (, ArithExpr, )],     (
            // [$, decimal],     |
            switch (lookahead.getExpression()) {
                case "decimal", "(", "true", "false", "sin", "cos", "max", "min" ->
                        throw new MissingOperatorException(); // MissingOperatorException
                case "&", "|", "!" -> throw new TypeMismatchedException();
            }
        }
        if (this.detectArithOp(symbols)) {
            // [$, ArithExpr, ^, (, ArithExpr, -],     )
            // [$, BoolExpr, ?, ArithExpr, :],     $
            // [$, decimal],     ?
            // [$, BoolExpr, ?, ArithExpr, >, ArithExpr, *, decimal],     :
            switch (lookahead.getExpression()) {
                case "+", "-", "*", "/", "^", "neg", ")", ",", ">", ">=", "<", "<=", "=", "<>", "&", "|", "!", "$" ->
                        throw new MissingOperandException(); // MissingOperandException
                case "?", ":" -> throw new TypeMismatchedException(); // TypeMismatchedException

            }
        }
        if (this.detectBoolExpr(symbols)) {
            // [$, (, BoolExpr, )],     +
            // [$, ArithExpr, ^, (, BoolExpr, )],     $
            // [$, sin, (, ArithExpr, >, decimal],     )
            switch (lookahead.getExpression()) {
                case "+", "-", "*", "/", "^", "neg", ":", "(", ")", ",", "$" ->
                        throw new TypeMismatchedException(); // TypeMismatchedException
            }
        }
        if (this.detectMaxAndMinFunctionWithArith(symbols)) {
            // [$, ArithExpr, +, max, (, ArithExpr, +, decimal],     )
            // [$, min, (, decimal],     )
            switch (lookahead.getExpression()) {
                case ")", "," -> throw new MissingOperandException(); // MissingOperandException
            }
        }
        if (this.detectSinAndCosFunctionWithArith(symbols)) {
            // [$, sin, (, ArithExpr, +, decimal],     ,
            // [$, sin, (, decimal],     ,
            switch (lookahead.getExpression()) {
                case "," -> throw new FunctionCallException(); // FunctionCallException
            }
        }


        System.out.println(reducePath); // debug
        throw new SyntacticException();
    }

    /**
     * 判断符号栈的结尾是否是算术表达式，这包括：ArithExpr、 (  ArithExpr  )
     *
     * @param symbols 符号栈
     * @return 是否是算术表达式
     */
    private boolean detectArithExpr(LinkedList<Grammar.Symbol> symbols) {
//        if (symbols.getLast().getExpression().equals("ArithExpr")) return true;
//        else if (symbols.getLast().getExpression().equals(")")) {
//            String[] tmp = new String[]{symbols.get(symbols.size() - 3).getExpression(), symbols.get(symbols.size() - 2).getExpression(), symbols.get(symbols.size() - 1).getExpression()};
//            return tmp[0].equals("(") && tmp[1].equals("ArithExpr") && tmp[2].equals(")");
//        }
//        return false;
        switch (symbols.getLast().getExpression()) {
            case "ArithExpr", "decimal":
                return true;
            default:
        }
        List<String> buffer;
        if (symbols.size() < 3) return false;
        buffer = symbols.subList(symbols.size() - 3, symbols.size()).stream().map(Grammar.Symbol::getExpression).toList();
        return buffer.equals(List.of("(", "ArithExpr", ")"));
    }

    /**
     * 判断符号栈的结尾是否是算术运算符，这包括：+ - * / ^
     *
     * @param symbols 符号栈
     * @return 是否是算术运算符
     */
    private boolean detectArithOp(LinkedList<Grammar.Symbol> symbols) {
        return switch (symbols.getLast().getExpression()) {
            case "+", "-", "*", "/", "^", "?", ":" -> true;
            default -> false;
        };
    }

    /**
     * 判断符号栈的结尾是否是布尔表达式，这包括：BoolExpr、 (  BoolExpr  )
     *
     * @param symbols 符号栈
     * @return 是否是布尔表达式
     */
    private boolean detectBoolExpr(LinkedList<Grammar.Symbol> symbols) {
        switch (symbols.getLast().getExpression()) {
            case "BoolExpr", "true", "false":
                return true;
            default:
        }
        List<String> buffer;
        if (symbols.size() < 3) return false;
        buffer = symbols.subList(symbols.size() - 3, symbols.size()).stream().map(Grammar.Symbol::getExpression).toList();
        if (buffer.equals(List.of("(", "BoolExpr", ")"))) return true;
        if (!buffer.get(0).equals("ArithExpr") && !buffer.get(0).equals("decimal")) return false;
        if (!new HashSet<>(List.of(">", ",", "=", ">=", "<=", "<>")).contains(buffer.get(1))) return false;
        if (!buffer.get(2).equals("ArithExpr") && !buffer.get(2).equals("decimal")) return false;
        return true;
    }

    /**
     * 判断符号栈的结尾是否是最值函数的一半，这包括：max  (  ArithExpr  +  decimal、 min  (  decimal
     *
     * @param symbols 符号栈
     * @return 是否是最值函数的一半
     */
    private boolean detectMaxAndMinFunctionWithArith(LinkedList<Grammar.Symbol> symbols) {
        List<String> buffer;
        if (symbols.size() < 3) return false;
        buffer = symbols.subList(symbols.size() - 3, symbols.size()).stream().map(Grammar.Symbol::getExpression).toList();
        if (buffer.equals(List.of("max", "(", "decimal"))) return true;
        if (buffer.equals(List.of("min", "(", "decimal"))) return true;
        if (buffer.equals(List.of("max", "(", "ArithExpr"))) return true;
        if (buffer.equals(List.of("min", "(", "ArithExpr"))) return true;
        if (symbols.size() < 5) return false;
        buffer = symbols.subList(symbols.size() - 5, symbols.size()).stream().map(Grammar.Symbol::getExpression).toList();
        if (!buffer.get(0).equals("max") && !buffer.equals("min")) return false;
        if (!buffer.get(1).equals("(")) return false;
        if (!buffer.get(2).equals("ArithExpr") && !buffer.get(2).equals("decimal")) return false;
        if (!new HashSet<>(List.of("+", "-", "*", "/", "^", "neg")).contains(buffer.get(3))) return false;
        if (!buffer.get(4).equals("ArithExpr") && !buffer.get(4).equals("decimal")) return false;
        return true;
    }

    /**
     * 判断符号栈的结尾是否是三角函数的一半，这包括：sin  (  ArithExpr  +  decimal、 cos  (  decimal
     *
     * @param symbols 符号栈
     * @return 三角函数的一半
     */
    private boolean detectSinAndCosFunctionWithArith(LinkedList<Grammar.Symbol> symbols) {
        List<String> buffer;
        if (symbols.size() < 3) return false;
        buffer = symbols.subList(symbols.size() - 3, symbols.size()).stream().map(Grammar.Symbol::getExpression).toList();
        if (buffer.equals(List.of("cos", "(", "decimal"))) return true;
        if (buffer.equals(List.of("sin", "(", "decimal"))) return true;
        if (symbols.size() < 5) return false;
        buffer = symbols.subList(symbols.size() - 5, symbols.size()).stream().map(Grammar.Symbol::getExpression).toList();
        if (buffer.equals(List.of("cos", "(", "ArithExpr", "+", "decimal"))) return true;
        return buffer.equals(List.of("sin", "(", "ArithExpr", "+", "decimal"));
    }

//    /**
//     * 判断符号栈的结尾是否是三角函数的一半，这包括：sin  (  ArithExpr  +  decimal、 cos  (  decimal
//     *
//     * @param symbols 符号栈
//     * @return 三角函数的一半
//     */
//    private boolean detectSinAndCosFunctionWithoutArith(LinkedList<Grammar.Symbol> symbols) {
//        List<String> buffer;
//        if (symbols.size() < 3) return false;
//        buffer = symbols.subList(symbols.size() - 3, symbols.size()).stream().map(Grammar.Symbol::getExpression).toList();
//        if (buffer.equals(List.of("cos", "(", "decimal"))) return true;
//        if (buffer.equals(List.of("sin", "(", "decimal"))) return true;
//        if (symbols.size() < 5) return false;
//        buffer = symbols.subList(symbols.size() - 5, symbols.size()).stream().map(Grammar.Symbol::getExpression).toList();
//        if (buffer.equals(List.of("cos", "(", "ArithExpr", "+", "decimal"))) return true;
//        return buffer.equals(List.of("sin", "(", "ArithExpr", "+", "decimal"));
//    }


    private static class Lookahead {
        /**
         * @return 下一位 token 对应的终结符
         */
        public Grammar.Terminal terminal() {
            return this.lookaheadTerminal;
        }

        /**
         * @return 下一位 token
         */
        public MyScanner.Token token() {
            return this.lookaheadToken;
        }

        /**
         * 取出 token 列表的下一个 token，并解码成 grammar 的 terminal
         */
        public void update() {
            // 到达结尾
            if (this.ptr == this.tokens.size()) {
                this.lookaheadTerminal = (Grammar.Terminal) this.grammar.getSymbol("$");
                this.lookaheadToken = null;
                return;
            }
            // 取出并特殊处理
            this.lookaheadToken = this.tokens.get(this.ptr++);
            if (this.lookaheadToken instanceof MyScanner.DecimalToken) { // 数字
                this.lookaheadTerminal = (Grammar.Terminal) this.grammar.getSymbol("decimal");
            } else if (this.lookaheadToken instanceof MyScanner.ArithmeticOperator1DToken) { // 负号
                this.lookaheadTerminal = (Grammar.Terminal) this.grammar.getSymbol("neg");
            } else { // 其它 8 种： Boolean、ArithmeticOp2D、ArithmeticOp3D、CompareOp、LogicalOp1D、LogicalOp2D、DelimiterOp、Function
                this.lookaheadTerminal = (Grammar.Terminal) this.grammar.getSymbol(this.lookaheadToken.getExpression());
            }
        }

        /**
         * 下一位的 token 对应的 terminal
         */
        private Grammar.Terminal lookaheadTerminal = null;
        /**
         * 下一位的 token
         */
        private MyScanner.Token lookaheadToken = null;

        /**
         * token 列表的指针，值为当前的 lookahead 的下标
         */
        private int ptr = 0;

        /**
         * token 列表
         */
        private final List<MyScanner.Token> tokens;

        /**
         * 转换用的文法
         */
        private final Grammar grammar;


        /**
         * 构造函数，构造后可立即使用（第 0 位）。
         *
         * @param tokens  token 列表
         * @param grammar 转义用的文法
         */
        public Lookahead(List<MyScanner.Token> tokens, Grammar grammar) {
            this.tokens = tokens;
            this.grammar = grammar;
            this.update();
        }


    }


//    private final Grammar grammar;

    /**
     * parser 表
     */
    private final ParserTable parserTable;


    /**
     * 构造函数
     *
     * @param parserTable parser 表
     */
    public SimpleParser(ParserTable parserTable) {
        this.parserTable = parserTable;
//        this.parserTable.getGrammar() = parserTable.getGrammar();
    }
}
